import dis
import new
from assembler import Assembler, RETURN_VALUE, CALL_FUNCTION, LOAD_FAST

class property(object):
    
    def __init__(self, func):
        self.__func = func
        varnames = self.__func.func_code.co_varnames
        self.__has_get = 'get' in varnames
        self.__has_set = 'set' in varnames
        self.__has_del = 'delete' in varnames
        
    def __delete__(self, instance):
        if not self.__has_del:
            raise IndexError('{0} cannot be deleted'.format(self.__func.func_name))
            
        a = Assembler()
        
        #return delete() -> it should not return a value, its just a hack like the whole module ;-)
        a.append(LOAD_FAST(self.__func.func_code.co_varnames.index('delete')))
        a.append(CALL_FUNCTION(0, 0))
        a.append(RETURN_VALUE())
        
        #add only the parts before the default "return None"
        code = self.__func.func_code.co_code[:-4] + a.code
        _code = self.__func.func_code
        
        self.__func.func_code = new.code(_code.co_argcount, _code.co_nlocals,
                          _code.co_stacksize, _code.co_flags,
                          code,
                          _code.co_consts, _code.co_names, _code.co_varnames,
                          _code.co_filename, _code.co_name, _code.co_firstlineno,
                          _code.co_lnotab, _code.co_freevars, _code.co_cellvars)
                          
        
        ret = self.__func(instance)
        self.__func.func_code = _code
        return ret
        
    def __get__(self, instance, owner):
        if not self.__has_get:
            raise IndexError('{0} is writeonly'.format(self.__func.func_name))
            
        a = Assembler()
        
        #return get()
        a.append(LOAD_FAST(self.__func.func_code.co_varnames.index('get')))
        a.append(CALL_FUNCTION(0, 0))
        a.append(RETURN_VALUE())
        
        #add only the parts before the default "return None"
        code = self.__func.func_code.co_code[:-4] + a.code
        _code = self.__func.func_code
        
        self.__func.func_code = new.code(_code.co_argcount, _code.co_nlocals,
                          _code.co_stacksize, _code.co_flags,
                          code,
                          _code.co_consts, _code.co_names, _code.co_varnames,
                          _code.co_filename, _code.co_name, _code.co_firstlineno,
                          _code.co_lnotab, _code.co_freevars, _code.co_cellvars)
                          
        
        ret = self.__func()#instance)
        self.__func.func_code = _code
        return ret
        
    def __set__(self, instance, value):
        if not self.__has_get:
            raise IndexError('{0} is readonly'.format(self.__func.func_name))
        a = Assembler()
        
        #return set(value)
        a.append(LOAD_FAST(self.__func.func_code.co_varnames.index('set')))
        a.append(CALL_FUNCTION(0, 0))
        a.append(RETURN_VALUE())
        
        #add only the parts before the default "return None"
        code = self.__func.func_code.co_code[:-4] + a.code
        _code = self.__func.func_code
        
        self.__func.func_code = new.code(_code.co_argcount, _code.co_nlocals,
                          _code.co_stacksize, _code.co_flags,
                          code,
                          _code.co_consts, _code.co_names, _code.co_varnames,
                          _code.co_filename, _code.co_name, _code.co_firstlineno,
                          _code.co_lnotab, _code.co_freevars, _code.co_cellvars)
                          
        self.__func.func_globals.update({'value' : value})
        ret = self.__func()#instance)
        self.__func.func_code = _code
        return ret
        
if __name__ == '__main__':
    class Example(object):

        @property
        def f(self):
            def get():
                return 'hallo' 
                
            def set():
                print value
                
            def delete():
                print 'delete'
                
    e = Example()
    print e.f
    e.f = 'other value than hallo'
    print e.f
    del e.f

